import { parse, RowVal } from './excelUtil';
import menuExcelDescriptor from './menuExcelDescriptor';
// let filePath = 'd:\\test.xls';
// let workbookData = parse(filePath, testExcelDescriptor);




const ORDER_TYPE_NAME: { [k: string]: string } = {  // TODO 从数据库构建
    DINE_IN: 'D',
    DELIVERY: 'DL',
    PICKUP: 'PK',
    TOGO: 'TG',
    ONLINE_TOGO: 'OTG',
    ONLINE_PICKUP: 'OPK',
    ONLINE_DELIVERY: 'OD',
    KIOSK_DINE_IN: 'KDI',
    KIOSK_TOGO: 'KTG',
    EMENU_DINE_IN: 'EDI',
    SELF_DINE_IN: 'SDI',
    CUSTOM_ORDER_TYPE1: 'COT1',
    CUSTOM_ORDER_TYPE2: 'COT2',
    CUSTOM_ORDER_TYPE3: 'COT3',
    CUSTOM_ORDER_TYPE4: 'COT4',
    KIOSK_PICKUP: 'KPK',
}




importMenu('000046', 'xxx', 'd:\\menuData.xlsx');


function importMenu(businessId: string, templateId: string, filePath: string) {
    //1.解析Excel数据
    //2.组织成对应的数据结构
    //3.合并、入库

    let workbookData = parse(filePath, menuExcelDescriptor);
    // printWorkbook(workbookData);

    let orderTypes = workbookData.get('OrderType')?.[0] || {};
    let visibleOrderTypes: Array<string> = getVisibleOrderTypes(orderTypes);    // 所有可见时只有1个元素，且值为ALL

    // group
    let groupRows = workbookData.get('Group') || [];
    validateGroups(groupRows);
    let groups = toPosGroup(businessId, templateId, groupRows);
    let groupIds = splitIntoGroups(groups, g => g['name'], g => g.bizId); // name --> bizId

    // options
    let optionRows = workbookData.get('Options') || [];
    validateOptions(optionRows);
    let posOptionGroups = splitIntoOptionGroups(optionRows, businessId);
    let options = toPosOption(posOptionGroups);

    // category
    let categoryRows = workbookData.get('Category') || [];
    validateCategory(categoryRows, groupIds, posOptionGroups);
    let categories = toPosCategory(businessId, templateId, categoryRows, groupIds, visibleOrderTypes, posOptionGroups);
    let categoryIds = splitIntoGroups(categories, c => c['name'], (c) => c.bizId);  // name --> bizId

    // comboSections
    let comboSectionRows = workbookData.get('ComboSection') || [];
    validateComboSections(comboSectionRows);



    let itemRows = workbookData.get('Item') || [];
    validateItem(itemRows, categoryIds, posOptionGroups);
    let items = toPosItem(businessId, templateId, itemRows, categoryIds, visibleOrderTypes, posOptionGroups);
    // console.info(JSON.stringify(categories, null, 4))

    // let globalOptionRows = workbookData.get('GlobalOption') || [];
    // let globalOptionGroups = splitIntoGroups(globalOptionRows, 'categoryName');

    // // 引用的记录，反向更新
    // let groupRefBy = new Map<string, Array<object>>();
    // let categoryRefBy = new Map<string, Array<object>>();
    // let optionRefBy = new Map<string, Array<object>>();

}


function toPosItem(businessId: string, templateId: string, itemRows: RowVal[], categoryIds: Map<string, any>, visibleOrderTypes: string[], optionGroups: Map<string, any>) {
    let bizId = getBizId(`menu_item-${businessId}`, itemRows.length);
    let now = Date.now();
    let visibleAll = visibleOrderTypes.includes('ALL');

    for (let row of itemRows) {
        let printers = row['printers'].val || [];

        let optionGroupNames = row['optionGroupNames'].val as Array<string> || [];
        let options = [];
        for (let ogn of optionGroupNames) {
            let tempOps = optionGroups.get(ogn) as Array<any> || [];
            tempOps.forEach(opt => delete opt.businesses);
            options.push(...tempOps);
        }

        let item = {
            type: "item",
            businessId,
            bizId: `${businessId}-mi-${bizId++}`,
            name: row['name'].val,
            nameZh: row['secondLangName'].val,
            number: "",
            desc: row['description'].val,
            sendKitchen: printers.length > 0,
            kitchenPrinterIds: printers,
            categoryIds: [],
            visibleAll: visibleAll ? 1 : 2,
            visibleOrderTypeIds: visibleAll ? [] : visibleOrderTypes,
            itemType: "Regular Item",
            priceStrategy: "Base Price",
            quickCombo: false,
            price: row['price'].val,
            tare: null,
            displayKitchenName: "",
            displayPosName: "",
            img: null,
            color: "#8AC2EE",
            tagIds: [],
            hiddenItem: row['hidden'].val == 'T',
            disabled: false,
            optionMax: "",
            optionFullScreen: false,
            options,
            comboSections: [],
            basePrice: {},
            createAt: now,
            updateAt: now,
            deleted: 0,
            seq: 999999,
            templates: [
                {
                    templateId,
                    deleted: 0,
                    createAt: now,
                    updateAt: now
                }
            ]
        }
    }

}


function splitIntoOptionGroups(optionRows: RowVal[], businessId: string) {
    let optionGroups = splitIntoGroups(optionRows, o => o['optionGroupName'].val) as Map<string, Array<RowVal>>;
    let posOptionGroups = new Map<string, any>();
    let bizId = getBizId(`menu_category-${businessId}`, optionRows.length);

    let now = Date.now();
    let optionIds = new Map<string, string>(); // 同名的都是做同一个 -- bizId相同
    for (let groupName of optionGroups.keys()) {
        let optionsOfGroup = optionGroups.get(groupName) || [];
        let posOptions = [];

        for (let row of optionsOfGroup) {
            let name = row['name'].val;
            let printers = row['printers'].val || [];
            let xxBizId = optionIds.has(name) ? (optionIds.get(name) as string) : `${businessId}-mo-${bizId++}`;
            optionIds.set(name, xxBizId);

            let option = {
                name,
                nameZh: row['secondLangName'].val,
                type: 'option',
                bizId: xxBizId,
                price: row['price'].val,
                sendKitchen: printers.length > 0,
                kitchenPrinterIds: printers,
                img: null,
                color: '#8AC2EE',
                disabled: false,
                optionMax: row['maxSelected'].val,
                seq: 999999,
                businessId,
                deleted: 0,
                createAt: now,
                updateAt: now,
                businesses: [
                    {
                        businessId,
                        deleted: 0,
                        createAt: now,
                        updateAt: now
                    }
                ]
            };
            posOptions.push(option);
        }
        posOptionGroups.set(groupName, posOptions);
    }
    return posOptionGroups;
}

function toPosCategory(businessId: string, templateId: string, categoryRows: RowVal[], groupIds: Map<string, string>, visibleOrderTypes: Array<string>, optionGroups: Map<string, any>) {
    let bizId = getBizId(`menu_category-${businessId}`, categoryRows.length);
    let now = Date.now();

    let visibleAll = visibleOrderTypes.includes('ALL');
    return categoryRows.map(row => {
        let groupName = row['groupName'].val;
        let groupId = groupIds.get(groupName)?.[0];

        let optionGroupNames = row['optionGroupNames'].val as Array<string> || [];
        let options = [];
        for (let ogn of optionGroupNames) {
            let tempOps = optionGroups.get(ogn) as Array<any> || [];
            tempOps.forEach(opt => delete opt.businesses);
            options.push(...tempOps);
        }

        return {
            businessId,
            bizId: `${businessId}-mc-${bizId++}`,
            groupId,
            name: row['name'].val,
            nameZh: row['secondLangName'].val,
            desc: row['description'].val,
            taxIds: [],
            taxZeroQuantity: 0,
            allowDiscount: row['allowDiscount'].val == 'T',
            visibleAll: visibleAll ? 1 : 2,
            visibleOrderTypeIds: visibleAll ? [] : visibleOrderTypes,
            options,
            required: false,
            hibachiMode: false,
            displayKitchenName: row['kitchenName'].val,
            displayPosName: row['posName'].val,
            img: null,
            color: "#8AC2EE",
            courseId: null,
            createAt: now,
            updateAt: now,
            deleted: 0,
            seq: 999999,
            templates: [
                {
                    templateId: templateId,
                    deleted: 0,
                    createAt: now,
                    updateAt: now,
                }
            ]
        }
    });
}



function toPosOption(posOptionGroups: Map<string, any>) {
    let options = [];
    let optionIds = new Map<string, null>();    // bizId去重

    for (let groupName of posOptionGroups.keys()) {
        let optionsOfGroup = posOptionGroups.get(groupName) || [];
        for (let option of optionsOfGroup) {
            if (!optionIds.has(option['bizId'])) {
                options.push(Object.assign({}, option));    // 复制一份，不受其他修改的干扰
                optionIds.set(option['bizId'], null);
            }
        }
    }
    return options;
}


function toPosGroup(businessId: string, templateId: string, groupRows: RowVal[]) {
    let bizId = getBizId(`menu_group-${businessId}`, groupRows.length);
    let now = Date.now();
    let groups = groupRows.map(groupRow => {
        return {
            bizId: `${businessId}-mg-${bizId++}`,
            name: groupRow['name'].val,
            nameZh: groupRow['secondLangName'].val,
            desc: groupRow['description'].val,
            openHourIds: [],
            businessId: businessId,
            createAt: now,
            updateAt: now,
            deleted: 0,
            seq: 999999,
            templates: [
                {
                    templateId: templateId,
                    createAt: now,
                    updateAt: now,
                }
            ]
        };
    });
    return groups;
}

function getBizId(bizType: string, size?: number) {
    if (size == null) {
        size = 1;
    }
    size = Math.max(size, 1);   // 至少1，即使size是负数
    // TODO
    return 1;
}

function getVisibleOrderTypes(orderTypes: RowVal) {
    let visibleOrderTypes: Array<string> = [];
    if (orderTypes['ALL'].val == 'Y') {
        visibleOrderTypes.push('ALL');
    } else {
        for (let ot in orderTypes) {
            if (orderTypes[ot].val == 'Y') {
                visibleOrderTypes.push(ORDER_TYPE_NAME[ot]);
            }
        }
    }
    return visibleOrderTypes;
}

function splitIntoGroups(dataSet: Array<any>, keyFn: (ele: any) => string, eleFn?: (r: any) => any) {
    let groups = new Map<string, any>();
    for (let ele of dataSet) {
        let groupName = keyFn(ele);
        let elesOfGroup = groups.get(groupName) || [];
        elesOfGroup.push(eleFn ? eleFn(ele) : ele);
        groups.set(groupName, elesOfGroup);
    }
    return groups;
}

function printWorkbook(workbookData: Map<string, RowVal[]>) {
    for (let sheetName of workbookData.keys()) {
        console.info(sheetName);
        console.info(JSON.stringify(workbookData.get(sheetName), null, 4));
    }
}

function validateGroups(groupRows: RowVal[]) {
    let groupNames = new Map<string, string>();
    for (let groupRow of groupRows) {
        let nameVal = groupRow['name']; // group的name不可重复
        let existAddress = groupNames.get(nameVal.val);
        if (existAddress) {
            throw new Error(`the groupName('${nameVal.val}') is duplicate: ${nameVal.address} --> ${existAddress}`);
        }
        groupNames.set(nameVal.val, nameVal.address);
    }
}

function validateOptions(optionRows: RowVal[]) {
    let optionGroups = splitIntoGroups(optionRows, (row) => row['optionGroupName'].val) as Map<string, Array<RowVal>>;
    for (let group of optionGroups.keys()) {
        let optionsOfGroup = optionGroups.get(group) || [];
        let optionNames = new Map<string, string>();
        for (let option of optionsOfGroup) {    // 同组内的name不能重复
            let nameVal = option['name'];
            let existAddress = optionNames.get(nameVal.val);
            if (existAddress) {
                throw new Error(`the optionName('${nameVal.val}') is duplicate: ${nameVal.address} --> ${existAddress}`);
            }
            optionNames.set(nameVal.val, nameVal.address);
        }
    }
}
function validateCategory(categoryRows: RowVal[], groups: Map<string, any>, optionGroups: Map<string, any>) {

    let categoryNames = new Map<string, string>();  // 一个sheet中的name不可重复
    for (let row of categoryRows) {
        let nameVal = row['name'];

        let groupNameVal = row['groupName'];
        if (!groups.has(groupNameVal.val)) {
            throw new Error(`the group('${groupNameVal.val}') referenced by category('${nameVal.val}' [${groupNameVal.address}]) not found`);
        }

        let existAddress = categoryNames.get(nameVal.val);
        if (existAddress) {
            throw new Error(`the categoryName('${nameVal.val}') is duplicate: ${nameVal.address} --> ${existAddress}`);
        }
        categoryNames.set(nameVal.val, nameVal.address);

        let optionGroupNames = row['optionGroupNames'].val as Array<any> || [];
        let optionGroupNotFound = optionGroupNames.some(n => !optionGroups.has(n));
        if (optionGroupNotFound) {
            throw new Error(`optionGroup('${optionGroupNames}') referenced by category('${nameVal.val}' [${row['optionGroupNames'].address}]) not found`);
        }
    }
}

function validateItem(itemRows: RowVal[], categoryIds: Map<string, any>, optionGroups: Map<string, any>) {
    let itemNames = new Map<string, string>();  // 一个sheet中的name不可重复
    for (let row of itemRows) {
        let nameVal = row['name'];

        let categoryNameVal = row['categoryName'];
        if (!categoryIds.has(categoryNameVal.val)) {
            throw new Error(`the category('${categoryNameVal.val}') referenced by item('${nameVal.val}' [${categoryNameVal.address}]) not found`);
        }

        let existAddress = itemNames.get(nameVal.val);
        if (existAddress) {
            throw new Error(`the itemName('${nameVal.val}') is duplicate: ${nameVal.address} --> ${existAddress}`);
        }
        itemNames.set(nameVal.val, nameVal.address);

        let optionGroupNames = row['optionGroupNames'].val as Array<any> || [];
        let optionGroupNotFound = optionGroupNames.some(n => !optionGroups.has(n));
        if (optionGroupNotFound) {
            throw new Error(`optionGroup('${optionGroupNames}') referenced by item('${nameVal.val}' [${row['optionGroupNames'].address}]) not found`);
        }
    }
}

function validateComboSections(csRows: RowVal[]) {

}

